﻿using Service.src.Model.Folw;
using Service.src.NW.Enum;
using Service.src.NW.Model.Folw;
using Service.src.NW.Model;
using Service.src.NW.Model.Drive;
using Service.src.Flow.JavaScript;
using System.Collections.Generic;
using DeviceManage.PLC;
using System.Threading.Tasks;
using System.Threading;
using System;
using Service.src.NW.Model.Enum;
using Service.src.NW.Model.Folw.PLC;

namespace Service.src.NW.Processor.Core
{
    public class PlcProcessor : BaseProcessor
    {
        /// <summary>
        /// PLC节点的处理逻辑
        /// </summary>
        /// <param name="NodeInfo"></param>
        /// <param name="input"></param>
        public static void Run(NodeProcessor NodeInfo, NodeInputParam input)
        {
            NodeStatusEnum nodeStatus = NodeStatusEnum.SUCCESS;
            if (!(bool)(AppData.Runings?.ContainsKey(NodeInfo._FlowId)))
            {
                nodeStatus = NodeStatusEnum.ERROR;
                Complete(NodeInfo, nodeStatus);
                return;
            }
            if (input == null)
            {
                SendNodeLoggerError(NodeInfo, "节点输入参数异常", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_NULL);
                SetNodeOutParamValue(NodeInfo, NodeOutParamNameEnum.NODE_STATUS, NodeStatusEnum.ERROR);

                nodeStatus = NodeStatusEnum.ERROR;
                Complete(NodeInfo, nodeStatus);
                return;
            }
            if (NodeInfo.PLCDevice == null)
            {
                SendNodeLoggerError(NodeInfo, "节点未找到对应的plc请检查是否连续成功", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_DATASOURCE);
                SetNodeOutParamValue(NodeInfo, NodeOutParamNameEnum.NODE_STATUS, NodeStatusEnum.ERROR);

                nodeStatus = NodeStatusEnum.ERROR;
                Complete(NodeInfo, nodeStatus);
                return;
            }
        
            if (!NodeInfo.PLCDevice.LinkSucceed)
            {
                SendNodeLoggerError(NodeInfo, "PLC未连接，请检查IP等参数是否正确，修改后重启生效。", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_DATASOURCE);
                nodeStatus = NodeStatusEnum.ERROR;
                Complete(NodeInfo, nodeStatus);
                return;
            }

            if (NodeInfo.nodeType == NodeTypeEnum.PLC_READ)
            {
                List<object> Values = new List<object>();
                foreach (NodeInputPlcReadParam read in input.NodePLC.ReadParams)
                {
                    SysPlcDriverAddress Offset = AppData.GetSysPlcDriverAddress(read.DataSourceId);
                    if (string.IsNullOrEmpty(Offset.Address))
                    {
                        nodeStatus = NodeStatusEnum.ERROR;
                        SendNodeLoggerError(NodeInfo, $"{NodeInfo.id}：参数配置异常,未配置PLC地址 ", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_NULL);
                        continue;
                    }
                    var PlcValue = NodeInfo.PLCDevice.Read(Offset.Address);
                    if (PlcValue == null)
                    {
                        nodeStatus = NodeStatusEnum.ERROR;
                        SendNodeLoggerError(NodeInfo, $"{NodeInfo.id}：读取PLC值失败，地址：{Offset.Address},请检查地址是否正确", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_NULL);
                        continue;
                    }
                    CheckNodeIsEnd(input, input.NodePLC.EndConditions, PlcValue.ToString());
                    read.Value = PlcValue;
                    SetNodeOutParamValue(NodeInfo, read, PlcValue);
                    Values.Add(PlcValue);
                    SendNodeLoggerInfo(NodeInfo, $"读取节点PLC值：({Offset.Name}){Offset.Address} = {PlcValue}", RuntimeErrorCodeEnum.SUCCESS);
                }
                SetNodeOutParamValue(NodeInfo, NodeOutParamNameEnum.DEVICE_READ_RESULT, Values);
                SetNodeOutParamValue(NodeInfo, NodeOutParamNameEnum.NODE_STATUS, nodeStatus);
            }
            if (NodeInfo.nodeType == NodeTypeEnum.PLC_WRITE)
            {
                bool WriteRestlt = false;
                foreach (NodeInputPlcWriteParam write in input.NodePLC.WriteParams)
                {
                    if (string.IsNullOrEmpty(write.DataSource.ToString())
                        || write.DataSourceId <= 0 || write.AddressId <= 0)
                    {
                        WriteRestlt = false;
                        SendNodeLoggerError(NodeInfo, $"{NodeInfo.id}参数配置异常,未配置PLC地址 ", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_NULL);
                        continue;
                    }
                    write.Content = GetNodeInputParamValue(NodeInfo, write);
                    SysPlcDriverAddress address = AppData.GetSysPlcDriverAddress(write.AddressId);
                    if (string.IsNullOrEmpty(address.Address))
                    {
                        WriteRestlt = false;
                        SendNodeLoggerError(NodeInfo, $"{NodeInfo.id}参数配置异常,未配置PLC地址 ", RuntimeErrorCodeEnum.NODE_INPUT_PARAM_NULL);
                        continue;
                    }
                    try
                    {
                        NodeInfo.PLCDevice.Write(address.Address, write.Content);
                        SendNodeLoggerInfo(NodeInfo, $"写入节点PLC值：{address.Address} = {write.Content}", RuntimeErrorCodeEnum.SUCCESS);
                        WriteRestlt = true;
                    }
                    catch (Exception ex)
                    {
                        SendNodeLoggerError(NodeInfo, $"{NodeInfo.id}写入节点PLC值失败：{address.Address} = {write.Content}",
                           ex, RuntimeErrorCodeEnum.SUCCESS);
                    }
                }
                SetNodeOutParamValue(NodeInfo, NodeOutParamNameEnum.NODE_STATUS, WriteRestlt ? NodeStatusEnum.SUCCESS : NodeStatusEnum.ERROR);
            }
            NodeInfo.Code = 1;
            if (NodeInfo.nodeType == NodeTypeEnum.PLC_READ && !input.IsNodeContinue)
            {
                SendNodeLoggerError(NodeInfo, $"结束条件不满足，继续等待", RuntimeErrorCodeEnum.NODE_OUT_DATA_NOT_MATCH);
                Thread.Sleep(1000);
                Run(NodeInfo, input);
            }
            else
            {
                Complete(NodeInfo, nodeStatus);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="Read"></param>
        /// <param name="v"></param>
        /// <returns></returns>
        private static bool CheckNodeIsEnd(NodeInputParam input, List<EndCondition> Read, string v)
        {
            bool IsNodeContinue = true;
            if (Read == null)
            {
                input.IsNodeContinue = IsNodeContinue;
                return true;
            }

            int iv = -9999999;
            if (v.GetType() == typeof(int))
            {
                iv = int.Parse(v);
            }

            foreach (EndCondition item in Read)
            {
                if (!IsNodeContinue)
                {
                    break;
                }
                if (string.IsNullOrEmpty(item.Operator.ToString()))
                {
                    continue;
                }
                switch (item.Operator)
                {
                    case OperatorEnum.ET:
                        IsNodeContinue = v == item.Value1;
                        break;
                    case OperatorEnum.GT:
                        IsNodeContinue = iv > int.Parse(item.Value1);
                        break;
                    case OperatorEnum.LT:
                        IsNodeContinue = iv < int.Parse(item.Value1);
                        break;
                    case OperatorEnum.BETWEEN:
                        IsNodeContinue = iv >= int.Parse(item.Value1)
                            && iv <= int.Parse(item.Value2);
                        break;
                    case OperatorEnum.NOT_BETWEEN:
                        IsNodeContinue = iv < int.Parse(item.Value1)
                            || iv > int.Parse(item.Value2);
                        break;
                    case OperatorEnum.CONTAINS:
                        IsNodeContinue = v.Contains(item.Value1);
                        break;
                    case OperatorEnum.NOT_CONTAINS:
                        IsNodeContinue = !v.Contains(item.Value1);
                        break;
                    case OperatorEnum.EMPTY:
                        IsNodeContinue = string.IsNullOrEmpty(v?.Trim());
                        break;
                    case OperatorEnum.NOT_EMPTY:
                        IsNodeContinue = !string.IsNullOrEmpty(v?.Trim());
                        break;
                }
            }
            input.IsNodeContinue = IsNodeContinue;
            return true;
        }

        /// <summary>
        /// 监听PLC的读写操作
        /// </summary>
        public static void OnPLC()
        {
            foreach (KeyValuePair<int, SysPlcDriverAddress> Item in AppData.CachePLCAddressList)
            {
                try
                {
                    SysPlcDriverAddress Offset = Item.Value;
                    if (!AppData.IPLCHelpers.ContainsKey(Offset.DriverId))
                    {
                        continue;
                    }
                    if (Offset == null || string.IsNullOrEmpty(Offset.Address.Trim()))
                    {
                        DeviceMessageResult error = new DeviceMessageResult(Offset.DriverType, $"错误代码：{(int)RuntimeErrorCodeEnum.DEVICE_PLC_ADDRESS}，PLC地址信息空，");
                        JavaScriptEvent.Send(JavaScriptEventEnum.DEVICE_LOGGER, error);
                        continue;
                    }
                    IPLCHelper PLC = AppData.IPLCHelpers[Offset.DriverId];
                    if (PLC == null)
                    {
                        DeviceMessageResult error = new DeviceMessageResult(Offset.DriverType, $"错误代码：{(int)RuntimeErrorCodeEnum.DEVICE_NOT_CONNECT}，PLC未连接，设备ID{Item.Key}");
                        JavaScriptEvent.Send(JavaScriptEventEnum.DEVICE_LOGGER, error);
                        continue;
                    }

                    // RunTimeCycleRead(PLC, Offset);
                    RunTimeCycleWrite(PLC, Offset);
                }
                catch (Exception ex)
                {
                    DeviceMessageResult error = new DeviceMessageResult();
                    error.Type = DeviceTypeEnum.PLC;
                    error.Exce = ex;
                    error.Message = "监听出错了";
                    JavaScriptEvent.Send(JavaScriptEventEnum.DEVICE_LOGGER, error);
                }
            }
        }

        /// <summary>
        /// 运行时一直读，如需要触发流程等
        /// </summary>
        /// <param name="item"></param>
        /// <exception cref="NotImplementedException"></exception>
        private static void RunTimeCycleRead(IPLCHelper PLC, SysPlcDriverAddress Offset)
        {
            Task.Run(() =>
            {
                while (true)
                {
                    PLC.Read<bool>(Offset.Address);
                    Thread.Sleep(1000);
                }
            });
        }

        /// <summary>
        /// 运行时一直写，如心跳等
        /// </summary>
        /// <param name="Item"></param>
        private static void RunTimeCycleWrite(IPLCHelper PLC, SysPlcDriverAddress Offset)
        {
            if (Offset.Cycle)
            {
                Task.Run(() =>
                {
                    while (Offset.Cycle)
                    {
                        PLC.Write(Offset.Address, Offset.Value1);
                        Thread.Sleep(Offset.CycleTime);
                        if (!string.IsNullOrEmpty(Offset.Value2))
                        {
                            PLC.Write(Offset.Address, Offset.Value2);
                            Thread.Sleep(Offset.CycleTime);
                        }
                    }
                });
            }
        }
    }
}
